/*
 * Copyright (c) 2018, Nordic Semiconductor
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package no.nordicsemi.android.blinky;

import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import com.example.otalib.boads.Constant;
import com.example.otalib.boads.WorkOnBoads;
import com.example.otalib.boads.msg;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.blinky.adapter.DiscoveredBluetoothDevice;
import no.nordicsemi.android.blinky.utils.BitmapUtils;
import no.nordicsemi.android.blinky.utils.DataHandlerUtils;
import no.nordicsemi.android.blinky.utils.SPUtils;
import no.nordicsemi.android.blinky.utils.Utils;
import no.nordicsemi.android.blinky.wekeupble.WakeupManager;
import no.nordicsemi.android.blinky.wekeupble.WakeupManagerCallbacks;

@SuppressWarnings("ConstantConditions")
public class HtxotaActivity extends AppCompatActivity implements WakeupManagerCallbacks, LoaderManager.LoaderCallbacks<Cursor> {
    public static final String EXTRA_DEVICE = "no.nordicsemi.android.blinky.EXTRA_DEVICE";
    public static final String TAG = HtxotaActivity.class.getSimpleName();


    @BindView(R.id.file_name)
    TextView fileNameView;
    @BindView(R.id.file_size)
    TextView fileSizeView;
    @BindView(R.id.address)
    TextView address;
    @BindView(R.id.progressBar)
    ProgressBar mProgressBar;
    private String deviceName;
    private String deviceAddress;
    private WakeupManager wakeupManager;
    private ProgressDialog dialog;

    private static final String EXTRA_URI = "uri";


    private Uri fileStreamUri;
    private String filePath;
    private static final int ENABLE_BT_REQ = 0;
    private static final int SELECT_FILE_REQ = 1;
    private static final int SELECT_INIT_FILE_REQ = 2;
    private String ispMac;

    private WorkOnBoads mWorkOnBoads;
    public static final int MSG_OTA_COMPLETE = 0X04;
    public static final int MSG_NO_FILE = 0X06;

    private boolean mDeviceReady;
    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg == null)
                return;

            switch (msg.arg1) {

                case MSG_OTA_COMPLETE:
                    Log.i(TAG, "MSG_OTA_COMPLETE");
                    mWorkOnBoads.resetDevice();
                    mProgressBar.setProgress(0);
                    Toast.makeText(HtxotaActivity.this, "OTA升级成功", Toast.LENGTH_LONG).show();
                    finish();
                    break;


                case Constant.MSG_ARG1_PROGRESS_BAR_MAX:
                    int len1 = msg.arg2;
                    Log.i(TAG, "MSG_ARG1_PROGRESS_BAR_MAX->" + len1);
                    mProgressBar.setMax(len1);
                    break;

                case Constant.MSG_ARG1_PROGRESS_BAR_UPDATA:
                    int len2 = msg.arg2;
                    Log.i(TAG, "MSG_ARG1_PROGRESS_BAR_UPDATA->" + len2);
                    mProgressBar.setProgress(len2);
                    break;

                case Constant.MSG_ARG1_SEND_OTA_DATA:
                    byte[] otaData = (byte[]) msg.obj;
                    Log.e(TAG, "data->" + DataHandlerUtils.bytesToHexStr(otaData));
                    wakeupManager.sendOtaData(otaData);

                    break;
                case Constant.MSG_ARG1_SEND_OTA_CMD: {
                    byte[] sendData = (byte[]) msg.obj;
                    Log.e(TAG, "cmd->" + DataHandlerUtils.bytesToHexStr(sendData));
                    wakeupManager.sendOtaCmd(sendData);

                    break;
                }
            }
        }
    };
    private DiscoveredBluetoothDevice device;
    private BluetoothAdapter adapter;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ota);
        ButterKnife.bind(this);
        adapter = BluetoothAdapter.getDefaultAdapter();


        mWorkOnBoads = new WorkOnBoads(this, handler);


        final Intent intent = getIntent();
        device = intent.getParcelableExtra(EXTRA_DEVICE);
        deviceName = device.getName();
        deviceAddress = device.getAddress();

        final Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(deviceName);
        getSupportActionBar().setSubtitle(deviceAddress);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        wakeupManager = new WakeupManager(this);
        wakeupManager.setGattCallbacks(this);

        connect();


        //获取之前保存的文件
        String fileName = SPUtils.getString(this, SPUtils.FILENAME, "");
        long fileSize = SPUtils.getLong(this, SPUtils.FILESIZE, 0L);
        String uri = SPUtils.getString(this, SPUtils.FILEURI, "");
        if (!TextUtils.isEmpty(uri)) {
            fileStreamUri = Uri.parse(uri);
            Log.i(TAG, fileStreamUri.toString());

        }

        fileNameView.setText(fileName);
        fileSizeView.setText(String.format("%d bytes", fileSize));

    }


    @Override
    public void onDataReceived(BluetoothDevice device, Data data) {
        Log.i(TAG, "onDataReceived");

        byte[] byteArrayExtra = data.getValue();
        if (byteArrayExtra == null) {
            return;
        }
        String hexStr = DataHandlerUtils.bytesToHexStr(byteArrayExtra);
        Log.i(TAG, "onDataReceived->" + hexStr);

        List<Integer> datas = DataHandlerUtils.bytesToArrayList(byteArrayExtra);

        if (hexStr != null) {
            if (datas.get(0) == 0xAB && datas.get(4) == 0x28 && datas.get(5) == 0x80) {
                String[] strings = hexStr.split(" ");
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = strings.length - 1; i >= 9; i--) {
                    Log.i(TAG, strings[i]);
                    stringBuilder.append(strings[i]);
                    if (i != 9) {
                        stringBuilder.append(":");
                    }
                }
                ispMac = stringBuilder.toString().toUpperCase();
                Log.i(TAG, "ispMac：" + ispMac);
                address.setText(ispMac);


            }
        }

    }

    @Override
    public void onDataSent(BluetoothDevice device, Data data) {
        Log.i(TAG, "onDataSent-->" + DataHandlerUtils.bytesToHexStr(data.getValue()));

    }

    @Override
    public void onDataSent1(BluetoothDevice device, Data data) {
        Log.i(TAG, "onDataSent1-->" + DataHandlerUtils.bytesToHexStr(data.getValue()));

    }

    @Override
    public void onDataSent2(BluetoothDevice device, Data data) {
        Log.i(TAG, "onDataSent2-->" + DataHandlerUtils.bytesToHexStr(data.getValue()));

    }

    @Override
    public void onDeviceConnecting(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceConnecting");
        showDialog();

    }


    @Override
    public void onDeviceConnected(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceConnected");

    }

    @Override
    public void onDeviceDisconnecting(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceDisconnecting");

    }

    @Override
    public void onDeviceDisconnected(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceDisconnected");
        hideDialog();
        mDeviceReady = false;
//        Toast.makeText(HtxotaActivity.this, "蓝牙断开", Toast.LENGTH_SHORT).show();

    }

    @Override
    public void onLinkLossOccurred(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onLinkLossOccurred");

    }

    @Override
    public void onServicesDiscovered(@NonNull BluetoothDevice device, boolean optionalServicesFound) {
        Log.i(TAG, "onServicesDiscovered");

    }

    @Override
    public void onDeviceReady(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceReady");
        hideDialog();
        mDeviceReady = true;
        Toast.makeText(HtxotaActivity.this, "蓝牙连接成功", Toast.LENGTH_LONG).show();


    }


    @Override
    public void onBondingRequired(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onBondingRequired");

    }

    @Override
    public void onBonded(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onBonded");

    }

    @Override
    public void onBondingFailed(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onBondingFailed");

    }

    @Override
    public void onError(@NonNull BluetoothDevice device, @NonNull String message, int errorCode) {
        Log.i(TAG, "onError");
        hideDialog();
    }

    @Override
    public void onDeviceNotSupported(@NonNull BluetoothDevice device) {
        Log.i(TAG, "onDeviceNotSupported");
        hideDialog();

    }


    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");

    }

    private void showDialog() {
        if (dialog == null) {
            dialog = new ProgressDialog(this);
            dialog.show();
            Log.d(TAG, "showDialog");
        }


    }

    private void hideDialog() {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
            Log.d(TAG, "hideDialog");
        }
    }

    public void openFileChooser(View view) {
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//        intent.setType("application/zip");
        intent.setType("application/octet-stream");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, SELECT_FILE_REQ);
        }
    }


    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK)
            return;

        switch (requestCode) {
            case SELECT_FILE_REQ: {
                // clear previous data
                filePath = null;
                fileStreamUri = null;

                final Uri uri = data.getData();

                if (uri.getScheme().equals("file")) {
                    final String path = uri.getPath();
                    final File file = new File(path);
                    filePath = path;

                    updateFileInfo(file.getName(), file.length());
                } else if (uri.getScheme().equals("content")) {
                    fileStreamUri = uri;
                    final Bundle extras = data.getExtras();
                    if (extras != null && extras.containsKey(Intent.EXTRA_STREAM))
                        fileStreamUri = extras.getParcelable(Intent.EXTRA_STREAM);

                    final Bundle bundle = new Bundle();
                    bundle.putParcelable(EXTRA_URI, uri);
                    getLoaderManager().restartLoader(SELECT_FILE_REQ, bundle, this);

                    SPUtils.putString(HtxotaActivity.this, SPUtils.FILEURI, fileStreamUri.toString());
                }
                break;
            }

            default:
                break;
        }
    }

    private void updateFileInfo(String fileName, long fileSize) {
        fileNameView.setText(fileName);
        fileSizeView.setText(String.format("%d bytes", fileSize));
        SPUtils.putString(HtxotaActivity.this, SPUtils.FILENAME, fileName);
        SPUtils.putLong(HtxotaActivity.this, SPUtils.FILESIZE, fileSize);

    }


    @Override
    public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
        final Uri uri = args.getParcelable(EXTRA_URI);
        return new CursorLoader(this, uri, null /* all columns, instead of projection */, null, null, null);
    }

    @Override
    public void onLoaderReset(final Loader<Cursor> loader) {
        fileNameView.setText(null);
        fileSizeView.setText(null);
        filePath = null;
        fileStreamUri = null;
    }

    @Override
    public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
        if (data != null && data.moveToNext()) {
            final String fileName = data.getString(data.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)/* 0 DISPLAY_NAME */);
            final int fileSize = data.getInt(data.getColumnIndex(MediaStore.MediaColumns.SIZE) /* 1 SIZE */);
            String filePath = null;
            final int dataIndex = data.getColumnIndex(MediaStore.MediaColumns.DATA);
            if (dataIndex != -1)
                filePath = data.getString(dataIndex /* 2 DATA */);
            if (!TextUtils.isEmpty(filePath))
                this.filePath = filePath;

            updateFileInfo(fileName, fileSize);
        } else {
            fileNameView.setText(null);
            fileSizeView.setText(null);
            filePath = null;
            fileStreamUri = null;
        }
    }

    public void onUploadClicked(View view) {

        //检查是否连接
        if (!mDeviceReady) {
            Toast.makeText(HtxotaActivity.this, "设备未连接！请连接上Huntersun-BLE", Toast.LENGTH_SHORT).show();
            return;

        }
        //检查是否进入ota，并且有ota服务
        boolean support = SPUtils.getBoolean(HtxotaActivity.this, SPUtils.SUPPORTOTA, false);
        if (!support) {
            Toast.makeText(HtxotaActivity.this, "未发现OTA服务,请确定进入ota", Toast.LENGTH_SHORT).show();
            return;
        }
        //检查文件
        if (fileStreamUri == null) {
            Log.e(TAG, "fileStreamUri is null");
            Toast.makeText(HtxotaActivity.this, "未获取到文件，请重新选文件", Toast.LENGTH_LONG).show();
            return;
        }

        byte[] tmp_read = Utils.readSDFile(HtxotaActivity.this, fileStreamUri);
        Log.i(TAG, "要发送的固件包数据" + DataHandlerUtils.bytesToHexStr(tmp_read));
        if (tmp_read == null) {
            Log.e(TAG, "tmp_read is null");
            Toast.makeText(HtxotaActivity.this, "未获取到文件，请重新选文件", Toast.LENGTH_LONG).show();
            return;
        }
        Toast.makeText(HtxotaActivity.this, "开始OTA", Toast.LENGTH_LONG).show();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "onUploadClicked");
                //升级app
                mWorkOnBoads.setEncrypt(false);
                int response = mWorkOnBoads.loadBinary(tmp_read, Constant.APPTYPE);
                Log.i(TAG, "response:" + response);
                if (response == 0) {
                    Message msg = Message.obtain();
                    msg.arg1 = MSG_OTA_COMPLETE;
                    handler.sendMessage(msg);
                }

            }
        }).start();


    }


    public void getAddress(View view) {
        Log.i(TAG, "getAddress");
        wakeupManager.send(bootloaderMac());

    }

    public void enterOta(View view) {
        Log.i(TAG, "enterOta");
        wakeupManager.send(bootloader());
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                finish();
                Toast.makeText(HtxotaActivity.this, "请连接Huntersun-BLE", Toast.LENGTH_LONG).show();
            }
        }, 1000);


    }

    public void resetDevice(View view) {
        Log.i(TAG, "resetDevice");
        mWorkOnBoads.resetDevice();
    }


    /**
     * 进入bootloader
     */
    public byte[] bootloader() {
        byte[] bytes = new byte[7];
        bytes[0] = (byte) 0xAB;
        bytes[1] = (byte) 0;
        bytes[2] = (byte) 4;
        bytes[3] = (byte) 0xFF;
        bytes[4] = (byte) 0x25;
        bytes[5] = (byte) 0x80;
        bytes[6] = (byte) 1;
        return bytes;
    }

    /**
     * bootloaderMac
     */
    public byte[] bootloaderMac() {
        byte[] bytes = new byte[7];
        bytes[0] = (byte) 0xAB;
        bytes[1] = (byte) 0;
        bytes[2] = (byte) 4;
        bytes[3] = (byte) 0xFF;
        bytes[4] = (byte) 0x28;
        bytes[5] = (byte) 0x80;
        bytes[6] = (byte) 1;
        return bytes;
    }

    @Override
    public void onDataReceived3(BluetoothDevice device, Data data) {
        byte[] byteArrayExtra = data.getValue();
        if (byteArrayExtra == null) {
            return;
        }
        String hexStr = DataHandlerUtils.bytesToHexStr(byteArrayExtra);
        Log.i(TAG, "onDataReceived3->" + hexStr);
        mWorkOnBoads.setBluetoothNotifyData(byteArrayExtra, Constant.CMDCHARACTER);

    }

    @Override
    public void onDataReceived4(BluetoothDevice device, Data data) {
        byte[] byteArrayExtra = data.getValue();
        if (byteArrayExtra == null) {
            return;
        }
        String hexStr = DataHandlerUtils.bytesToHexStr(byteArrayExtra);
        Log.i(TAG, "onDataReceived4->" + hexStr);
        mWorkOnBoads.setBluetoothNotifyData(byteArrayExtra, Constant.DATACHARACTER);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
        if (wakeupManager != null) {
            wakeupManager.close();
            wakeupManager = null;
        }
    }


//    public void connect(View view) {
//        if (mDeviceReady) {
//            Toast.makeText(HtxotaActivity.this, "已经连接", Toast.LENGTH_LONG).show();
//            return;
//        }
//        connect();
//    }

    private void connect() {
        BluetoothDevice remoteDevice = adapter.getRemoteDevice(deviceAddress);
        wakeupManager.connect(remoteDevice)
                .useAutoConnect(false)
//                .retry(3, 100)
                .timeout(6000)
                .enqueue();
    }
}
